package ipc;

//TODO: try a fixed orig prob horizon and see if it does any better
//TODO: spudd approx methods
//TODO: see how long it takes for simulator calls - else switch to mdpsim


import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import rddl.RDDL.DOMAIN;
import rddl.RDDL.INSTANCE;
import rddl.RDDL.NONFLUENTS;
import ipc.Relay;
import rddl.parser.parser;
import rddl.RDDL;

import java.util.Random;

import ipc.InstanceComparator;

class Main{
	private static RDDL rddlset = new RDDL();
	public static HashMap<String, Boolean> solve = new HashMap<String, Boolean>();
	public static int numInstances;
	public static Logger timeLog, spaceLog, XMLLog, paramLog, activityLog, rewardLog, exceptionLog;
	public static Logger spuddLog;
	public static Logger dataLog;
	private long timeRemaining;
	List<String> instances;
	Random _rand;
	
	public int readInstances(String filename){

	    InputStream is = null;
		try {
			is = new BufferedInputStream(new FileInputStream(filename));
		} catch (FileNotFoundException e) {
			exceptionLog.log("Instance list file not found\n"+e.getMessage());
			System.exit(1);
		}
		
		instances = new ArrayList<String>();
		StringBuilder sb = new StringBuilder();
		int c = 0;
		int numLines = 0;
		
		while( true ){
			try {
				c = is.read();
			} catch (IOException e) {
				exceptionLog.log("exception in reading file\n"+e.getMessage());
				System.exit(1);
			}
			if(c == -1 || c == '\n'){
				
				if(sb.length() != 0){
					instances.add(sb.toString());
					++numLines;
				}
				sb = null;
				if( c == -1 ) 
					break;
				sb = new StringBuilder();
			}
			else{
				sb.append((char)c);
			}
		}
		return numLines;
	}

	
	public Main(String[] args, long seed ){
		String dirName = args[7]+"_"+args[4];
		new File(dirName).mkdir();
		dirName += "/";
		
		int fHorizon = Integer.parseInt(args[4]);
		Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
		_rand = new Random(seed);
		long time = System.currentTimeMillis();
		rewardLog = new Logger(dirName+"rewards.log");
		rewardLog.start();
		
		exceptionLog = new Logger(dirName+"exceptions.log");
		exceptionLog.start();
		
		spuddLog = new Logger(dirName+"spudd.log");
		spuddLog.start();
		
		dataLog = new Logger(dirName+"data.log");
		dataLog.start();
		
		
		File f = new File(args[2]);
		if (f.isDirectory()) {
			for (File f2 : f.listFiles())
				if (!f2.getName().contains("pomdp")) {
					try {
						rddlset.addOtherRDDL(parser.parse(f2));
					} catch (Exception e) {
						Main.exceptionLog.log("exception in parsing rddl file\n"+e.getMessage());
					}
				}
		} else
			try {
				rddlset.addOtherRDDL(parser.parse(f));
			} catch (Exception e) {
				Main.exceptionLog.log("exception in parsing rddl file\n"+e.getMessage());
			}

		f = new File(args[2]+"/traffic_mdp.rddl.simulate");

		try{
			rddlset.addOtherRDDL(parser.parse(f)); 
		}catch(Exception e){
			e.printStackTrace();
		}

		
		for(String d : rddlset._tmDomainNodes.keySet())
			solve.put(d, true);
		
		numInstances = readInstances(args[3]);
		double hoursPerInstance = Double.parseDouble(args[6]);
		timeRemaining = (long)(numInstances*hoursPerInstance *60*60*1000);
		Collections.sort(instances, new InstanceComparator(rddlset));
		System.err.println(numInstances + " instances");
		int done = 0;
		
		SPUDDer spudder = new SPUDDer(instances, args[2], (long)(0.8d * timeRemaining/numInstances) );
		spudder.start();
		
		timeRemaining -= (System.currentTimeMillis()-time);
		
		for( String inst : instances){
			time = System.currentTimeMillis();
			INSTANCE _i = rddlset._tmInstanceNodes.get(inst);
		
			DOMAIN _d = rddlset._tmDomainNodes.get(_i._sDomain);
			NONFLUENTS _n = rddlset._tmNonFluentNodes.get(_i._sNonFluents);
			
			if( _i == null || _d == null || _n == null)
				throw new RuntimeException("could not load rddl");
				
				
			if( solve.get(_d._sDomainName) == false ){
				++done;
				continue;
			}
			
			timeLog = new Logger(dirName+inst+"time.log");
			timeLog.start();
			spaceLog = new Logger(dirName+inst+"space.log");
			spaceLog.start();
			XMLLog = new Logger(dirName+inst+"xml.log");
			XMLLog.start();
			paramLog = new Logger(dirName+inst+"param.log");
			paramLog.start();
			activityLog = new Logger(dirName+inst+"activity.log");
			activityLog.start();
			
			
			Relay.init(args[0], Integer.valueOf(args[1]));
			RDDL solving = new RDDL();
			solving.addDomain(_d);
			solving.addInstance(_i);
			solving.addNonFluents(_n);
			
			activityLog.log("Solving instance "+ inst + "horizon " + _i._nHorizon);
			System.out.println("Solving instance "+ inst + "horizon " + _i._nHorizon + " " + _i._hmObjects.size());
			
			//TODO: Compute baseline: random and noop
			
			Relay r = Relay.sessionInit(inst);
//			System.err.println("Time allowed " + r.timeAllowed);
//	        timeRemaining = r.timeAllowed*60*60*1000;
	        int yetToSolve = numInstances - done;
//	        System.err.println(timeRemaining/yetToSolve);
	        timeRemaining -= (System.currentTimeMillis()-time);
	        time = System.currentTimeMillis();
		System.out.println("Time remaining " + timeRemaining);
		long inst_time = timeRemaining/yetToSolve;
		TimedRounds tr=null;
		try {
			tr = new TimedRounds(solving, inst, inst + ".spudd", _i._nHorizon, 
inst_time, r.numRounds, _rand.nextLong(), fHorizon, args[7]);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		SPUDDer.kill(inst);
		SPUDDer.done(inst);
        System.gc();	
		timeRemaining -= (System.currentTimeMillis()-time);

		time = System.currentTimeMillis();
		double totalr = Relay.sessionEnd();//TODO: USE r
        Main.rewardLog.log("Total reward for " + inst + " " + totalr);
        Main.dataLog.log(inst+" "+tr.getMean()+" "+tr.getVariance());
		tr = null;
			
			//TODO: Get round reward and decide if you should continue in this direction
			++done;
			if( done >= numInstances ) break;
//			System.gc();
			Runtime RUNTIME = Runtime.getRuntime();
			System.err.println("[ Memory usage: " + 
					((RUNTIME.totalMemory() - RUNTIME.freeMemory())/1e6d) + "Mb / " + 
					(RUNTIME.totalMemory()/1e6d) + "Mb" + 
					" = " + (((double) (RUNTIME.totalMemory() - RUNTIME.freeMemory()) / 
									   (double) RUNTIME.totalMemory())) + " ]\n");
			timeRemaining -= (System.currentTimeMillis()-time);
		}
		System.exit(0);
	}
	
	
	public static void main(String[] args) throws Exception{
		/* 0 - server host name
		 * 1 - port number
		 * 2 - location of rddl files
		 * 3 - filename of list of instance names
		 * 4 - forward horizon
		 * 5 - run description
		 * 6 - hours per instance
		 * 7 - agent( NOOP, RANDOM, SPUDD, BOPP)
		 */
		if( args.length != 8 )
			throw new Exception("Usage error");
		try {
			new Main(args, new Random(System.currentTimeMillis()).nextLong()); 
		} catch (Exception e) {
			Main.exceptionLog.log("exception in main\n"+e.getMessage());
			e.printStackTrace();
		}
		
        System.exit(1);	
	}
}
